using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace hamster
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        //#region debug stuff
        //static VertexPositionColor[] _debug;
        //static Effect _debugShader;

        //public static void DrawDebugSquare(Matrix matrix, Color color)
        //{ 
        //    _debugShader.Parameters["Color"].SetValue(new Vector4(color.R, color.G, color.B, 1f));
        //    _debugShader.Parameters["View"].SetValue(Game1.viewMatrix);
        //    _debugShader.Parameters["Projection"].SetValue(Game1.projectionMatrix);
        //    _debugShader.Parameters["World"].SetValue(matrix);
        //    _debugShader.Begin();

        //    foreach (EffectPass pass in _debugShader.CurrentTechnique.Passes)
        //    {
        //        pass.Begin();
        //        device.DrawUserPrimitives<VertexPositionColor>(
        //            PrimitiveType.TriangleList,
        //            _debug,
        //            0,
        //            _debug.Length / 3
        //        );
        //        pass.End(); 
        //    }

        //    _debugShader.End();
        //}
        //void SetupDebug()
        //{
        //    GetEffect("Debug.fx", out _debugShader);
        //    _debug = new VertexPositionColor[] {

        //        new VertexPositionColor { Position = new Vector3(-1f, 0f, -1f) },
        //        new VertexPositionColor { Position = new Vector3(1f, 0f, -1f) },
        //        new VertexPositionColor { Position = new Vector3(1f, 0f, 1f) },

        //        new VertexPositionColor { Position = new Vector3(-1f, 0f, -1f) },
        //        new VertexPositionColor { Position = new Vector3(1f, 0f, 1f) },
        //        new VertexPositionColor { Position = new Vector3(-1f, 0f, 1f) },

        //        new VertexPositionColor { Position = new Vector3(-1f, -1f, 0f) },
        //        new VertexPositionColor { Position = new Vector3(1f, -1f, 0f) },
        //        new VertexPositionColor { Position = new Vector3(1f, 1f, 0f) },

        //        new VertexPositionColor { Position = new Vector3(-1f, -1f, 0f) },
        //        new VertexPositionColor { Position = new Vector3(1f, 1f, 0f) },
        //        new VertexPositionColor { Position = new Vector3(-1f, 1f, 0f) },

        //        new VertexPositionColor { Position = new Vector3(0f, -1f, -1f) },
        //        new VertexPositionColor { Position = new Vector3(0f, 1f, -1f) },
        //        new VertexPositionColor { Position = new Vector3(0f, 1f, 1f) },

        //        new VertexPositionColor { Position = new Vector3(0f, -1f, -1f) },
        //        new VertexPositionColor { Position = new Vector3(0f, 1f, 1f) },
        //        new VertexPositionColor { Position = new Vector3(0f, -1f, 1f) },
        //    };
        //}
        //#endregion

        GraphicsDeviceManager graphics;
        public static SpriteBatch spriteBatch;
        static GraphicsDevice device;
        public static Matrix viewMatrix = Matrix.Identity;
        public static Matrix projectionMatrix = Matrix.Identity;
        Wheel _wheel;
        Scene _scene;
        Firefly _firefly;
        public static Player _player;
        static EffectPool _effectPool;
        Effect _postshader;
        ObjectPlacer objectPlacer_;
        Texture2D testTexture;
        public static State state_;
        public static bool gameOverMan;

        RenderTarget2D glowBuffer;
        RenderTarget2D sceneBuffer;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            
            //graphics.IsFullScreen = true;
            graphics.PreferredBackBufferWidth = 960;
            graphics.PreferredBackBufferHeight = 540;
            
            Content.RootDirectory = "Content";
            _wheel = new Wheel();
            _scene = new Scene();
            _player = new Player(this);
            _firefly = new Firefly(_player);
            gameOverMan = false;
        }

        public static float time_duration = 0f;
        public static float Sin(float t)
        {
            return (float)Math.Sin(t);
        }
        public static float Cos(float t)
        {
            return (float)Math.Cos(t);
        }
        public static Random random = new Random();

        public static float RandomFloat
        {
            get
            {
                return (float)random.NextDouble();
            }
        }
        public static Vector3 RandomVector
        {
            get
            {
                return new Vector3(RandomFloat, RandomFloat, RandomFloat);
            }
        }

        protected override void Initialize()
        {
            objectPlacer_ = new ObjectPlacer(this);
            objectPlacer_.Initialise();
            base.Initialize();
        }
        
        public static RenderTarget2D CreateRenderTarget(GraphicsDevice device, int numberLevels, SurfaceFormat surface)
        {
            MultiSampleType type = device.PresentationParameters.MultiSampleType;

            // If the card can't use the surface format
            if (!GraphicsAdapter.DefaultAdapter.CheckDeviceFormat(DeviceType.Hardware,
                GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Format, TextureUsage.None,
                QueryUsages.None, ResourceType.RenderTarget, surface))
            {
                // Fall back to current display format
                surface = device.DisplayMode.Format;
            }
            // Or it can't accept that surface format with the current AA settings
            else if (!GraphicsAdapter.DefaultAdapter.CheckDeviceMultiSampleType(DeviceType.Hardware,
                surface, device.PresentationParameters.IsFullScreen, type))
            {
                // Fall back to no antialiasing
                type = MultiSampleType.None;
            }

            // Create our render target
            return new RenderTarget2D(device,
                device.PresentationParameters.BackBufferWidth, device.PresentationParameters.BackBufferHeight, numberLevels, surface,
                type, 0);

        }

        protected override void LoadContent()
        {
            device = GraphicsDevice;
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            _effectPool = new EffectPool();
            //SetupDebug();

            glowBuffer = CreateRenderTarget(GraphicsDevice, 1, SurfaceFormat.Rgba32);
            sceneBuffer = CreateRenderTarget(GraphicsDevice, 1, SurfaceFormat.Rgba32);

            GraphicsDevice.VertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionNormalTexture.VertexElements);
            // TODO: use this.Content to load your game content here\

            _wheel.Create(Content);
            _scene.Create(Content);
            _player.Create(Content);
            _firefly.Create(Content);
            objectPlacer_.ReloadContent(Content);

            Particles.instance.Create(Content);
            _postshader = Content.Load<Effect>("Postprocess");

        }

        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }


        protected override void Update(GameTime gameTime)
        {
            //if (Keyboard.GetState().IsKeyDown(Keys.R))
            //{
            //    objectPlacer_.ReloadContent();
            //    _scene.Reload();
            //    _wheel.Reload();

            //    Particles.instance.Create();
            //    /*
            //    _player.Reload();
            //    _firefly.Reload();
            //     */
            //    if (_postshader != null) _postshader.Dispose();
            //    GetEffect("Postprocess.fx", out _postshader);
                
            //}
            float dt = gameTime.ElapsedGameTime.Milliseconds / 1000f;
            time_duration += dt;
            viewMatrix = Matrix.CreateLookAt(
                _player._viewPosition,
                _player._viewTarget,
                _player._viewRot
                );

            projectionMatrix = Matrix.CreatePerspectiveFieldOfView( MathHelper.ToRadians(70f), 
                (float)GraphicsDevice.Viewport.Width / (float)GraphicsDevice.Viewport.Height, 1f, 1000f);

            if (!gameOverMan)
            {
                _wheel.Update(dt);
                _scene.Update(dt);
                // Check for player collisions with world objects
                objectPlacer_.Update(dt, _player);
            }
            _firefly.Update(dt);
            


            Particles.instance.Update(dt);
            // Check for player collisions with world objects
            objectPlacer_.Update(dt, _player);

            //Check for input
            _player.Update(dt);

            Window.Title = "Cosmic Hamster Wheel - SCORE: " + _player.score;
            base.Update(gameTime);
        }

        void PostProcess()
        {
            VertexPositionNormalTexture[] quad = new VertexPositionNormalTexture[] {

                new VertexPositionNormalTexture { Position = new Vector3(-1f, -1f, 0f), TextureCoordinate = new Vector2(0,1) },
                new VertexPositionNormalTexture { Position = new Vector3(1f, -1f, 0f), TextureCoordinate = new Vector2(1,1) },
                new VertexPositionNormalTexture { Position = new Vector3(1f, 1f, 0f), TextureCoordinate = new Vector2(1,0) },

                new VertexPositionNormalTexture { Position = new Vector3(-1f, -1f, 0f), TextureCoordinate = new Vector2(0,1) },
                new VertexPositionNormalTexture { Position = new Vector3(1f, 1f, 0f), TextureCoordinate = new Vector2(1,0) },
                new VertexPositionNormalTexture { Position = new Vector3(-1f, 1f, 0f), TextureCoordinate = new Vector2(0,0) },
            };

            GraphicsDevice.Clear(Color.Black);
            device.RenderState.AlphaBlendEnable = true;
            device.RenderState.SourceBlend = Blend.SourceAlpha;
            device.RenderState.DestinationBlend = Blend.InverseSourceAlpha;

            _postshader.Parameters["UserTexture"].SetValue(glowBuffer.GetTexture());
            _postshader.Parameters["SceneTexture"].SetValue(sceneBuffer.GetTexture());
            _postshader.Parameters["Saturation"].SetValue( 1f - _player.brightness);
            
            _postshader.Begin();

            foreach (EffectPass pass in _postshader.CurrentTechnique.Passes)
            {
                pass.Begin();
                device.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, quad, 0, quad.Length / 3);
                pass.End();
            }

            _postshader.End();

            device.RenderState.AlphaBlendEnable = false;
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            //First pass
            GraphicsDevice.SetRenderTarget(0, glowBuffer);
            GraphicsDevice.Clear(Color.Black);
            _scene.DrawGlow(GraphicsDevice);
            _firefly.DrawGlow(GraphicsDevice);

            //Second pass
            GraphicsDevice.SetRenderTarget(0, sceneBuffer);
            GraphicsDevice.Clear(Color.Black);
            GraphicsDevice.RenderState.CullMode = CullMode.None;

            _scene.DrawBackground(GraphicsDevice);
            _wheel.Draw(GraphicsDevice);
            


                objectPlacer_.Draw(GraphicsDevice);

                //Particles.instance.Draw(GraphicsDevice);
                _player.Draw(GraphicsDevice);
                _scene.Draw(GraphicsDevice);
                _firefly.Draw(GraphicsDevice);


                _wheel.DrawGrate(GraphicsDevice);
            

            //Post process
            GraphicsDevice.SetRenderTarget(0, null);
            PostProcess();


            //spriteBatch.Begin();
            //spriteBatch.Draw(testTexture, new Rectangle(0, 0, 30, 30), Color.White);
            //spriteBatch.End();

            base.Draw(gameTime);
        }

        internal static float RandomNegPos()
        {
            return (float) (Game1.random.NextDouble() - .5) * 2;
        }


        public static float StepCloserToZero(float value, float decrement)
        {
            if (value > 0)
                value -= decrement;
            else if (value < 0)
                value += decrement;
            if (Math.Abs(value) < decrement)
                value = 0;
            return value;
        }
    }
}
